home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C ++ / Frameworks / MacZoop 1.6.5 / Basic Classes / Z Sources / ZMenuBar.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-15  |  26.6 KB  |  1,064 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            ObjectMacZapp        -- a standard Mac OOP application template
  5. *
  6. *
  7. *
  8. *            ZMenuBar.cpp        -- the menubar manager object
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21.  
  22. #include    "MacZoop.h"
  23. #include    "ZCommander.h"
  24.  
  25. extern    ZCommander*        gCurHandler;
  26.  
  27. /*--------------------------------***  DESTRUCTOR  ***---------------------------------*/
  28.  
  29.  
  30. ZMenuBar::~ZMenuBar()
  31. {
  32.     if ( theMenuCmds )
  33.         ForgetObject( theMenuCmds );
  34.         
  35.     if ( theMenus )
  36.         ForgetObject( theMenus );
  37.  
  38.     ReleaseResource((Handle) mBarH );
  39. }    
  40.  
  41.  
  42. /*--------------------------------***  INITMENUBAR  ***--------------------------------*/
  43. /*
  44. initialise the menubar from a MBAR resource
  45. ---------------------------------------------------------------------------------------*/
  46.  
  47. void        ZMenuBar::InitMenuBar()
  48. {
  49.     // create an array for holding our command info
  50.     
  51.     FailNIL( theMenuCmds = new ZArray( sizeof( MenuCmd )));
  52.     FailNIL( theMenus = new ZArray( sizeof( MenuInfRec )));
  53.     
  54.     mbCount = 0;
  55.     miSeed = 1;
  56.     wmMenuID = 0;
  57.     
  58.     // load the MBAR resource
  59.     
  60.     mBarH = (short**) GetResource( 'MBAR', mBarID );
  61.     FailOSErr( ResError());
  62.     
  63.     HNoPurge((Handle) mBarH );
  64.     
  65.     // read in menus from the MBAR resource
  66.     
  67.     LoadMenus();
  68.     
  69.     HPurge((Handle) mBarH );
  70.     
  71.     // add the standard items (DA's) to the Apple menu
  72.     
  73.     AppendStdItems( kAppleMenuID );    
  74.     
  75.     menuCheckChar = checkMark;
  76.     
  77.     // note how many items are in the help menu so we can correctly
  78.     // identify any items we have added
  79.     
  80.     MenuHandle    helpMenuH;
  81.     
  82.     FailOSErr( HMGetHelpMenuHandle( &helpMenuH )); 
  83.     mHelpOffset = CountMenuItems( helpMenuH );
  84. }    
  85.  
  86.  
  87. /*-------------------------------***  CLICKMENUBAR  ***--------------------------------*/
  88. /*
  89. handle mouse click in the menubar
  90. ---------------------------------------------------------------------------------------*/
  91.  
  92. void        ZMenuBar::ClickMenuBar( const Point mousePt )
  93. {
  94.     long        mSelect;
  95.     MenuCmd*    mCmd;
  96.     
  97.     // initially disable all menu items
  98.     
  99.     DimMenus();
  100.     
  101.     // ask the command chain to reenable the relevant menu items
  102.  
  103.     if ( gCurHandler )
  104.         gCurHandler->UpdateMenus();
  105.  
  106.     // now track and select the menus
  107.     
  108.     mSelect = TrackMenuBar( mousePt );
  109.     
  110.     // dispatch the command
  111.     
  112.     DispatchCommand( mSelect );    
  113. }    
  114.  
  115.  
  116. /*-------------------------------***  TRACKMENUBAR  ***--------------------------------*/
  117. /*
  118. track the menubar, returning the item chosen.
  119. ---------------------------------------------------------------------------------------*/
  120.  
  121. long        ZMenuBar::TrackMenuBar( const Point mouse )    
  122. {
  123.     return MenuSelect( mouse );
  124. }
  125.  
  126.  
  127. /*------------------------------***  DISPATCHCOMMAND  ***------------------------------*/
  128. /*
  129. look up the command and send it up the chain
  130. ---------------------------------------------------------------------------------------*/
  131.  
  132. void    ZMenuBar::DispatchCommand( const long mSelect )
  133. {
  134.     MenuCmd        mCmd;
  135.     GrafPtr        savePort;
  136.     
  137.     GetPort( &savePort );
  138.     
  139.     // if an item was chosen, look up the command and send it up the chain
  140.     
  141.     mCmd.theCmd = noCommand;
  142.     
  143.     if ( HiWord( mSelect) != 0 && gCurHandler )
  144.     {
  145.         // if the nominated windows menu, handle the window selection
  146.         
  147.         if ( HiWord( mSelect ) == wmMenuID )
  148.             gWindowManager->SelectWindowFromMenu( LoWord( mSelect ));    
  149.  
  150.         FindMCmd( mSelect, &mCmd );
  151.         
  152.         if ( mCmd.theCmd != parentCmd )
  153.         {
  154.             // if the command was found, pass it up the chain. If not found, we still pass it
  155.             // up the chain, but this time as the direct menuID and itemID chosen. Commander
  156.             // classes can choose which of the two methods (or perhaps both?) to adopt.
  157.             
  158.             if ( mCmd.theCmd != noCommand )
  159.                 gCurHandler->HandleCommand( mCmd.theCmd );
  160.             else
  161.                 gCurHandler->HandleCommand( HiWord( mSelect ), LoWord( mSelect ));            
  162.         }
  163.  
  164.         SetPort( savePort );
  165.         SetTitleHilite( 0, FALSE );
  166.     }
  167. }    
  168.  
  169.  
  170. /*---------------------------------***  ENABLECMD  ***---------------------------------*/
  171. /*
  172. enable the menu item associated with the command
  173. ---------------------------------------------------------------------------------------*/
  174.  
  175. void        ZMenuBar::EnableCommand( const long cmd )
  176. {
  177.     short    m = 0, i = 0;
  178.     
  179.     FindCommand( cmd, &m, &i );
  180.     
  181.     if ( m && i )
  182.         EnableCommand( m, i );
  183. }    
  184.  
  185. /*---------------------------------***  ENABLECMD  ***---------------------------------*/
  186.  
  187. void        ZMenuBar::EnableCommand( const short menuID, const short itemID )
  188. {
  189.     MenuHandle    mH = FindMenuID( menuID );
  190.     
  191.     if ( mH )
  192.         EnableItem( mH, itemID );
  193. }    
  194.  
  195. /*---------------------------------***  DISABLECMD  ***--------------------------------*/
  196. /*
  197. disable the menu item associated with the command
  198. ---------------------------------------------------------------------------------------*/
  199.  
  200. void        ZMenuBar::DisableCommand( const long cmd )
  201. {
  202.     short    m = 0, i = 0;
  203.     
  204.     FindCommand( cmd, &m, &i );
  205.     
  206.     if ( m && i )
  207.         DisableCommand( m, i );
  208. }    
  209.  
  210. /*---------------------------------***  DISABLECMD  ***--------------------------------*/
  211.  
  212. void        ZMenuBar::DisableCommand( const short menuID, const short itemID )
  213. {
  214.     MenuHandle    mH = FindMenuID( menuID );
  215.     
  216.     if ( mH )
  217.         DisableItem( mH, itemID );
  218. }
  219.  
  220.  
  221. /*--------------------------------***  CHECKCOMMAND  ***-------------------------------*/
  222. /*
  223. check the menu command on or off.
  224. ---------------------------------------------------------------------------------------*/
  225.  
  226. void        ZMenuBar::CheckCommand( const long cmd, const Boolean checkOnOff )
  227. {
  228.     short    m = 0, i = 0;
  229.     
  230.     FindCommand( cmd, &m, &i );
  231.     
  232.     if ( m && i )
  233.         CheckCommand( m, i, checkOnOff );
  234. }
  235.  
  236.  
  237. /*--------------------------------***  CHECKCOMMAND  ***-------------------------------*/
  238.  
  239. void        ZMenuBar::CheckCommand( const short menuID, const short itemID, const Boolean checkOnOff )
  240. {
  241.     MenuHandle    mH = FindMenuID( menuID );
  242.     
  243.     if ( mH )
  244.         SetItemMark( mH, itemID, checkOnOff? menuCheckChar : noMark );
  245. }
  246.  
  247.  
  248. /*--------------------------------***  CHECKCOMMAND  ***-------------------------------*/
  249.  
  250. void        ZMenuBar::CheckCommand( const short menuID, Str255 itemString, const Boolean checkOnOff )
  251. {
  252.     // check the item with the text matching that passed (not case sensitive)
  253.     
  254.     MenuHandle    mH = FindMenuID( menuID );
  255.     Str255        iMatch;
  256.     
  257.     if ( mH )
  258.     {
  259.         short    m = CountMenuItems( mH );
  260.     
  261.         do
  262.         {
  263.             GetMenuItemText( mH, m, iMatch );
  264.             
  265.             if ( EqualString( itemString, iMatch, FALSE, TRUE ))
  266.             {
  267.                 SetItemMark( mH, m, checkOnOff? menuCheckChar : noMark );
  268.                 break;
  269.             }
  270.         }
  271.         while( --m );
  272.     }
  273. }
  274.  
  275.  
  276. /*-------------------------------***  SETCOMMANDTEXT  ***------------------------------*/
  277. /*
  278. set the text of a menu item to the desired string. Can be accessed by command or item.
  279. ---------------------------------------------------------------------------------------*/
  280.  
  281. void        ZMenuBar::SetCommandText( const long cmd, Str255 aText )
  282. {
  283.     short    m = 0, i = 0;
  284.     
  285.     FindCommand( cmd, &m, &i );
  286.     
  287.     if ( m && i )
  288.         SetCommandText( m, i, aText );
  289. }
  290.  
  291.  
  292. /*-------------------------------***  SETCOMMANDTEXT  ***------------------------------*/
  293.  
  294. void        ZMenuBar::SetCommandText( const short menuID, const short itemID, Str255 aText )
  295. {
  296.     MenuHandle    mH;
  297.     
  298.     mH = FindMenuID( menuID );
  299.     
  300.     if ( mH )
  301.         SetMenuItemText( mH, itemID, aText );
  302. }
  303.  
  304.  
  305. /*-------------------------------***  SETCOMMANDTEXT  ***------------------------------*/
  306.  
  307. void        ZMenuBar::SetCommandText( const long cmd, const short strListID, const short strIndex )
  308. {
  309.     Str255    aText;
  310.     
  311.     GetIndString( aText, strListID, strIndex );
  312.     if ( aText[0] > 0 )
  313.         SetCommandText( cmd, aText );
  314. }
  315.  
  316.  
  317. /*-------------------------------***  SETCOMMANDTEXT  ***------------------------------*/
  318.  
  319. void        ZMenuBar::SetCommandText( const short menuID, const short itemID, const short strListID, const short strIndex )
  320. {
  321.     Str255    aText;
  322.     
  323.     GetIndString( aText, strListID, strIndex );
  324.     if ( aText[0] > 0 )
  325.         SetCommandText( menuID, itemID, aText );
  326. }
  327.  
  328.  
  329. /*-----------------------------***  NOMINATEWINDOWSMENU  ***---------------------------*/
  330. /*
  331. This method can be used to set up an automatic "Windows" menu. To do this, call this
  332. method once your bar is built. This will nominate the menu with the ID passed as the
  333. Windows menu. Any items in the menu already will be retained and work as normal. As
  334. windows are added and deleted in the window manager, this will maintain the menu for you.
  335. ---------------------------------------------------------------------------------------*/
  336.  
  337. void        ZMenuBar::NominateWindowsMenu( const short menuID )    
  338. {
  339.     if ( wmMenuID == 0 )
  340.     {
  341.         MenuHandle    mH = FindMenuID( menuID );
  342.         
  343.         if ( mH )
  344.         {
  345.             wmMenuID = menuID;
  346.             
  347.             SetMenuDimming( menuID, disableCmdsOnly );
  348.             
  349.             // we need to work hand-in-hand with the window manager to
  350.             // keep track of the windows. The Window Manager is best placed to
  351.             // do all this, so we simply hand off the menu to it.
  352.             
  353.             gWindowManager->SetWindowsMenu( mH );
  354.         }
  355.     }
  356. }
  357.  
  358.  
  359. /*-------------------------------***  INSERTHELPITEM  ***------------------------------*/
  360. /*
  361. append the text to the help menu, and return the item number of the resulting item. To
  362. process this item, override HandleCommand and look for kHMHelpMenuID and the item returned
  363. from this method.
  364. ---------------------------------------------------------------------------------------*/
  365.  
  366. short        ZMenuBar::AppendHelpItem( Str255 itemText )        
  367. {
  368.     short        i;
  369.     MenuHandle    helpMenuH;
  370.     
  371.     FailOSErr( HMGetHelpMenuHandle( &helpMenuH ));
  372.     
  373.     i = mHelpOffset + 1;
  374.     AppendMenu( helpMenuH, itemText );    
  375.     
  376.     return i;
  377. }
  378.  
  379.  
  380. /*------------------------------***  APPENDMENUTOBAR  ***------------------------------*/
  381. /*
  382. This can be called to add menus "on the fly" into the bar. This reads a menu from a
  383. MENU or CMNU resource, parses its comands and inserts the menu. Menus can only be added
  384. to the end of the bar. You need to call UpdateMenuBar after this or a series of these.
  385. ---------------------------------------------------------------------------------------*/
  386.  
  387. void        ZMenuBar::AppendMenuToBar( const short menuID  )
  388. {
  389.     Handle    temp;
  390.     
  391.     temp = GetResource( 'CMNU', menuID );
  392.     
  393.     if ( temp )
  394.     {
  395.         LoadCMNUMenu( menuID );
  396.         ReleaseResource( temp );
  397.     }
  398.     else
  399.         LoadMenu( menuID );
  400.     
  401.     mbCount++;    
  402. }
  403.  
  404. /*-----------------------------***  REMOVEMENUFROMBAR  ***-----------------------------*/
  405. /*
  406. This can be called to remove a menu added with the above routine. You need to call
  407. UpdateMenuBar after this or a series of these.
  408. ---------------------------------------------------------------------------------------*/
  409.  
  410. void        ZMenuBar::RemoveMenuFromBar( const short menuID )
  411. {
  412.     MenuHandle    mH;
  413.     
  414.     FailNILParam( mH = GetMenuHandle( menuID ));    
  415.     
  416.     // remove the command entries for this menu and any submenus it is the parent of.
  417.     
  418.     UnloadMenu( mH );
  419.     mbCount--;
  420. }
  421.  
  422.  
  423.  
  424. /*---------------------------------***  LOADMENUS  ***---------------------------------*/
  425. /*
  426. for each menu in the MBAR, call LoadMenu. This will deal with hierarchical menus, etc.
  427. This method assumes mBarH is valid, and won't be purged.
  428. ---------------------------------------------------------------------------------------*/
  429.  
  430. void        ZMenuBar::LoadMenus( const Boolean autoInstall )
  431. {
  432.     short        i, menuID;
  433.     Handle        temp;
  434.     
  435.     // how many menus in MBAR resource? This is first item in resource.
  436.     
  437.     mbCount = (*mBarH)[0];
  438.     
  439.     // iterate through, looking for MENU or CMNU resources
  440.     
  441.     for ( i = 1; i <= mbCount; i++ )
  442.     {
  443.         menuID = (*mBarH)[i];
  444.         
  445.         // if CMNU resource is available, use that. Otherwise, use MENU resource,
  446.         // possibly parsing it for command numbers
  447.         
  448.         temp = GetResource( 'CMNU', menuID );
  449.         
  450.         if ( temp )
  451.         {
  452.             LoadCMNUMenu( menuID, FALSE, autoInstall );
  453.             ReleaseResource( temp );
  454.         }
  455.         else
  456.             LoadMenu( menuID, FALSE, autoInstall );
  457.  
  458.     }
  459. }    
  460.  
  461. /*----------------------------------***  DIMMENUS  ***---------------------------------*/
  462. /*
  463. dim all of the menu items, according to their flags.
  464. ---------------------------------------------------------------------------------------*/
  465.  
  466. void        ZMenuBar::DimMenus()
  467. {
  468.     // This originally operated by walking the low-memory global menuList, but that does
  469.     // not work when 3rd party extensions install extra menus. Thus we now use our private
  470.     // list of menus to do this operation so we only affect our own menus.
  471.     
  472.     short        i;
  473.     MenuInfRec    mRec;
  474.     
  475.     for ( i = 1; i <= mbCount; i++ )
  476.     {
  477.         // find this menu in our list, and dim it according to the
  478.         // flags there. (Thanks to Jean-Yves Pochez for the improvements to this method)
  479.         
  480.         mRec.macMenu = NULL;
  481.         theMenus->GetArrayItem( &mRec, i );
  482.     
  483.         if ( mRec.macMenu )
  484.         {
  485.             // menu is in our list, so dim it
  486.             
  487.             PredimMenu( mRec.macMenu );
  488.         }
  489.     }
  490.  
  491.     // if auto Windows menu in use, set that up as well
  492.     
  493.     if ( wmMenuID > 0 )
  494.         gWindowManager->BuildWindowsMenu();    
  495. }
  496.  
  497.  
  498. /*-------------------------------***  SETMENUDIMMING  ***------------------------------*/
  499. /*
  500. set the dimming options for a particular menu. By default, all menus are dimmed auto-
  501. matically, but if you don't want them dimmed, or partially dimmed, call this with the
  502. required options.
  503. ---------------------------------------------------------------------------------------*/
  504.  
  505. void        ZMenuBar::SetMenuDimming( const short menuID, const DimmingOptions dimOpts )
  506. {
  507.     MenuInfRec    mr;
  508.     Boolean        bUpdate;
  509.     
  510.     mr.macMenu = NULL;
  511.     
  512.     FindMenuInfo( menuID, &mr );
  513.     
  514.     if ( mr.macMenu )
  515.     {
  516.         // if this call will change the state of the dimTitle flag, we need to cause an
  517.         // update to the menubar so that the user sees the change
  518.         
  519.         bUpdate = (( mr.mDimming ^ dimOpts ) & dimTitle ) != 0;
  520.         
  521.         mr.mDimming = dimOpts;
  522.         theMenus->SetArrayItem( &mr, mr.mIndex );
  523.         
  524.         // update the bar if necessary
  525.         
  526.         if ( bUpdate )
  527.         {
  528.             // enable or disable title item
  529.             
  530.             if ( dimOpts & dimTitle )
  531.                 (*mr.macMenu)->enableFlags &= 0xFFFFFFFE;
  532.             else
  533.                 (*mr.macMenu)->enableFlags |= 1;
  534.             
  535.             UpdateMenuBar();
  536.         }
  537.     }
  538. }    
  539.  
  540.  
  541. /*----------------------------------***  LOADMENU  ***---------------------------------*/
  542. /*
  543. can be called recursively- try to load the menu with the ID and any submenus it refers
  544. to. If MENU resources are not found, try CMNU resources.
  545. ---------------------------------------------------------------------------------------*/
  546.  
  547. void        ZMenuBar::LoadMenu( const short menuID, Boolean isHMenu, Boolean autoInstall )
  548. {
  549.     short        i, mCount, cmdChar, subID;
  550.     MenuHandle    mH;
  551.     MenuCmd        mCmd;
  552.     MenuInfRec    mRec;
  553.     Str255        iText;
  554.     
  555.     FailNILRes( mH = GetMenu( menuID ));
  556.     
  557.     // set up info record for this menu
  558.     
  559.     mRec.menuID = menuID;
  560.     mRec.mIndex = miSeed++;
  561.     mRec.macMenu = mH;
  562.     mRec.mDimming = dimCommands;
  563.     mRec.mIsResource = TRUE;
  564.     
  565.     theMenus->AppendItem( &mRec );
  566.     
  567.     // look through the menu for submenus, and recursively add them
  568.     
  569.     mCount = CountMenuItems( mH );
  570.     
  571.     for( i = 1; i <= mCount; i++ )
  572.     {
  573.         GetMenuItemText( mH, i, iText );
  574.         
  575.         if ( iText[1] != '-' )
  576.         {
  577.             // make a command entry for the item
  578.             
  579.             mCmd.menuID = menuID;
  580.             mCmd.itemID = i;
  581.             mCmd.macMenu = mH;
  582.             
  583.             ParseMenuItem( iText, &mCmd.theCmd );
  584.             
  585.             // menus created using GetMenu are resources, CMNU are not. This is flagged so
  586.             // we can dispose of it correctly.
  587.             
  588.             mCmd.cmdFlags = autoUnCheck | ( isHMenu? 0 : isPrimaryMenu ) | menuIsResource;
  589.             mCmd.subMenuID = 0;
  590.             
  591.             SetMenuItemText( mH, i, iText );
  592.             GetItemCmd( mH, i, &cmdChar );
  593.             
  594.             if ( cmdChar == hMenuCmd )
  595.             {
  596.                 // has a submenu, so load it:
  597.                 
  598.                 GetItemMark( mH, i, &subID );
  599.                 
  600.                 // need to determine if submenu is a MENU or CMNU resource:
  601.                 
  602.                 Handle    cmnuH;
  603.                 
  604.                 cmnuH = GetResource( 'CMNU', subID );
  605.                 
  606.                 if ( cmnuH )
  607.                 {
  608.                     LoadCMNUMenu( subID, TRUE, autoInstall );
  609.                     ReleaseResource( cmnuH );
  610.                 }
  611.                 else
  612.                     LoadMenu( subID, TRUE, autoInstall );
  613.                     
  614.                 mCmd.subMenuID = subID;
  615.                 mCmd.theCmd = parentCmd;
  616.             }
  617.             
  618.             theMenuCmds->AppendItem( &mCmd );
  619.         }
  620.     }
  621.     
  622.     // add the menu to the system list
  623.     
  624.     if ( autoInstall )
  625.         InsertMenu( mH, isHMenu? hierMenu : 0 );
  626. }    
  627.  
  628. /*--------------------------------***  LOADCMNUMENU  ***-------------------------------*/
  629. /*
  630. ditto for CMNU resources- this can parse that format of resource.
  631. ---------------------------------------------------------------------------------------*/
  632.  
  633. void        ZMenuBar::LoadCMNUMenu( const short menuID, Boolean isHMenu, Boolean autoInstall )
  634. {
  635.     CMNUResHdl    cH;
  636.     MenuCmd        mCmd;
  637.     MenuInfRec    mRec;
  638.     MenuHandle    mH;
  639.     Ptr            cmP, cmItemText;
  640.     short        itemID;
  641.     
  642.     cH = ( CMNUResHdl ) GetResource( 'CMNU', menuID );
  643.     
  644.     if ( cH )
  645.     {
  646.         // we need to do two things with this menu- a) make a normal menu handle that
  647.         // can be inserted into the menu manager list, and b) a set of menuCmd records
  648.         // so that we can look up the command when an item is chosen.
  649.     
  650.         FailNIL( mH = NewMenu( menuID, (ConstStr255Param) &(*cH)->mTitle ));
  651.         
  652.         // set up info record for this menu
  653.         
  654.         mRec.menuID = menuID;
  655.         mRec.mIndex = miSeed++;
  656.         mRec.macMenu = mH;
  657.         mRec.mDimming = dimCommands;
  658.         mRec.mIsResource = FALSE;
  659.         
  660.         theMenus->AppendItem( &mRec );
  661.         
  662.         HLock((Handle) cH );
  663.         
  664.         // we need to iterate through the items in the CMNU and build our two structures
  665.         // as needed. To do this efficiently, we lock the handle and keep a running pointer.
  666.         
  667.         cmItemText = cmP = &(*cH)->mTitle + (*cH)->mTitle + 1;
  668.         itemID = 0;
  669.         
  670.         // we're now at the start of the first item's text. We now scan through each item
  671.         // building both the real menu and the command structure
  672.         
  673.         while( *cmItemText != 0 )
  674.         {
  675.             itemID++;
  676.             
  677.             cmP += *cmItemText + 1;        // point to top of "interesting" info
  678.             
  679.             // add text of item, making sure meta-characters are ignored. Note that
  680.             // a dividing line still works correctly
  681.             
  682.             AppendMenu( mH, "\px" );
  683.             SetMenuItemText( mH, itemID, (ConstStr255Param) cmItemText );
  684.             
  685.             // add command entry if not a dividing line
  686.             
  687.             if ( cmItemText[1] != '-' )
  688.             {
  689.                 // make menu item match info
  690.                 
  691.                 SetItemIcon    ( mH, itemID, ((CMNUEntryPtr) cmP)->iconID );
  692.                 SetItemCmd    ( mH, itemID, ((CMNUEntryPtr) cmP)->keyEqu );
  693.                 SetItemMark    ( mH, itemID, ((CMNUEntryPtr) cmP)->markChar );
  694.                 SetItemStyle( mH, itemID, ((CMNUEntryPtr) cmP)->iStyle );
  695.                 
  696.                 mCmd.menuID = menuID;
  697.                 mCmd.itemID = itemID;
  698.                 mCmd.subMenuID = 0;
  699.                 mCmd.macMenu = mH;
  700.                 mCmd.cmdFlags = autoUnCheck | ( isHMenu? 0 : isPrimaryMenu );
  701.                 mCmd.theCmd = noCommand;
  702.                 
  703.                 // if there's a sub-menu, we'll need to find and load it too. This involves
  704.                 // a recursion to this function or to LoadMenu.
  705.                 
  706.                 if (((CMNUEntryPtr) cmP)->keyEqu == hMenuCmd )
  707.                 {
  708.                     mCmd.subMenuID = ((CMNUEntryPtr) cmP)->markChar;
  709.                 
  710.                     // is this a CMNU or a MENU resource?
  711.                     
  712.                     Handle    cmnuH = GetResource( 'CMNU', mCmd.subMenuID );
  713.                     
  714.                     if ( cmnuH )
  715.                     {
  716.                         LoadCMNUMenu( mCmd.subMenuID, TRUE, autoInstall );
  717.                         ReleaseResource( cmnuH );
  718.                     }
  719.                     else
  720.                         LoadMenu( mCmd.subMenuID, TRUE, autoInstall );
  721.                         
  722.                     mCmd.theCmd = parentCmd;
  723.                 }
  724.                 else
  725.                 {
  726.                     // set up the command entry for the item. The command number
  727.                     // may follow a pad byte if it would otherwise be at an odd address, so the
  728.                     // data lies 4 or 5 bytes away from where cmP is now:
  729.                 
  730.                     if ((unsigned long) cmP & 1 )
  731.                         mCmd.theCmd = *(long*)( cmP + sizeof( CMNUEntry ) + 1 );
  732.                     else
  733.                         mCmd.theCmd = *(long*)( cmP + sizeof( CMNUEntry ));
  734.                 }
  735.                 // add the item to our command array
  736.                 
  737.                 theMenuCmds->AppendItem( &mCmd );
  738.             }    
  739.             
  740.             // set the pointers to the next item. This is 8 or 9 bytes away depending on
  741.             // whether the resulting address is odd or even
  742.             
  743.             cmP += 8;
  744.             if ((unsigned long) cmP & 1 )
  745.                 cmP++;
  746.                 
  747.             cmItemText = cmP;
  748.         }
  749.  
  750.         HUnlock((Handle) cH );
  751.         
  752.         // insert the menu we just built into the system list
  753.         
  754.         if ( autoInstall )
  755.             InsertMenu( mH, isHMenu? hierMenu : 0 );
  756.     }
  757. }
  758.  
  759.  
  760. /*---------------------------------***  UNLOADMENU  ***--------------------------------*/
  761. /*
  762. removes this menu from the command list and un-inserts itself. It also calls itself to
  763. deal with its submenus. Called by RemoveMenuFromBar. Use with care.
  764. ---------------------------------------------------------------------------------------*/
  765.  
  766. void        ZMenuBar::UnloadMenu( MenuHandle mH )
  767. {
  768.     long        m;
  769.     MenuCmd        mCmd;
  770.     Boolean        resStatus = FALSE;
  771.     Boolean        isResMenu = FALSE;
  772.  
  773.     for( m = theMenuCmds->CountItems(); m > 0; m-- )
  774.     {
  775.         theMenuCmds->GetArrayItem( &mCmd, m );
  776.         
  777.         // if this is one pertaining to the menu, delete it from the array. Also
  778.         // if its a parent item, call this again to delete the submenu too.
  779.     
  780.         if ( mH == mCmd.macMenu )
  781.         {
  782.             theMenuCmds->DeleteItem( m );
  783.         
  784.             // is this a parent?
  785.             
  786.             if ( mCmd.theCmd == parentCmd &&
  787.                  mCmd.subMenuID != 0 )
  788.             {
  789.                 // yes, so recurse and delete that too
  790.                 
  791.                 MenuHandle    smH = GetMenuHandle( mCmd.subMenuID );
  792.                 
  793.                 if ( smH )
  794.                     UnloadMenu( smH );
  795.             }
  796.             
  797.             if ( !resStatus )
  798.             {
  799.                 isResMenu = ( mCmd.cmdFlags & menuIsResource ) == menuIsResource;
  800.                 resStatus = TRUE;
  801.             }
  802.         }
  803.     }    
  804.     // remove the menu from the system menu list
  805.     
  806.     DeleteMenu((*mH)->menuID );
  807.     
  808.     // dispose or release the menu handle according to whether
  809.     // it was a resource or not
  810.     
  811.     if ( isResMenu )
  812.         ReleaseResource((Handle) mH );
  813.     else
  814.         DisposeMenu( mH );
  815. }    
  816.  
  817. /*---------------------------------***  PREDIMMENU  ***--------------------------------*/
  818. /*
  819. can be called recursively- dim this menu and any submenus it owns.
  820. ---------------------------------------------------------------------------------------*/
  821.  
  822. void        ZMenuBar::PredimMenu( MenuHandle theMenu )
  823. {
  824.     short            m, i, cmd, subID;
  825.     MenuInfRec        mi;
  826.     
  827.     if ( theMenu )
  828.     {
  829.         // initially set all items to be dimmed except the title ( we make one
  830.         // exception- the apple menu only has the first item dimmed );
  831.         
  832.         if ((*theMenu)->menuID == kAppleMenuID )
  833.             (*theMenu)->enableFlags = 0xFFFFFFF9;
  834.         else
  835.         {
  836.             // get the info rec for this menu
  837.             
  838.             FindMenuInfo((*theMenu)->menuID, &mi );
  839.             
  840.             // dim items according to dimming flags except title
  841.             // if title already dimmed, it is not re-enabled here.
  842.             
  843.             if (( mi.mDimming & 0x0F ) != neverDim )
  844.                 (*theMenu)->enableFlags &= 0x00000001;
  845.         
  846.             // look through the items for submenus
  847.             
  848.             m = CountMenuItems( theMenu );
  849.         
  850.             for( i = 1; i <= m; i++ )
  851.             {
  852.                 GetItemCmd( theMenu, i, &cmd );
  853.                 
  854.                 if ( cmd == hMenuCmd )
  855.                 {
  856.                     // Enable the parent item if permitted
  857.                     
  858.                     EnableItem( theMenu, i );
  859.                     GetItemMark( theMenu, i, &subID );
  860.                     
  861.                     // find the sub-menu
  862.                     
  863.                     MenuHandle    smMenu;
  864.                     
  865.                     smMenu = FindMenuID( subID );
  866.                     
  867.                     // dim the submenu
  868.                     
  869.                     PredimMenu( smMenu );
  870.                 }
  871.                 else
  872.                 {
  873.                     // if the item has no command and we don't want these dimming, enable it
  874.                     
  875.                     if ( mi.mDimming & dimCommands )
  876.                     {
  877.                         MenuCmd        mc;
  878.                         
  879.                         mc.theCmd = 0;
  880.                         FindMCmd((((long)(*theMenu)->menuID ) << 16 ) | i, &mc );
  881.                         
  882.                         if ( mc.theCmd == 0 )
  883.                             EnableItem( theMenu, i );
  884.                     }
  885.                     // if the item is checked, uncheck it
  886.                     
  887.                     GetItemMark( theMenu, i, &cmd );
  888.                     
  889.                     if ( cmd != noMark )
  890.                         SetItemMark( theMenu, i, noMark );
  891.                 }
  892.             }
  893.         }
  894.     }    
  895. }    
  896.  
  897. /*-------------------------------***  PARSEMENUITEM  ***-------------------------------*/
  898. /*
  899. extract command info from the item for TCL-style menu items
  900. ---------------------------------------------------------------------------------------*/
  901.  
  902. void        ZMenuBar::ParseMenuItem( Str255 iText, long* aCmd )
  903. {
  904.     *aCmd = noCommand;
  905.     
  906.     // a valid command is associated with the item by appending a hash sysmbol (#),
  907.     // followed by the command number as a string. Here we extract the number and
  908.     // modify the string to exclude it.
  909.     
  910.     unsigned char    i = 1;
  911.     Str15            subStr;
  912.     
  913.     // search for # char
  914.     
  915.     while((iText[i] != '#') && (i < iText[0]))
  916.         i++;
  917.     
  918.     if ( i < iText[0] )
  919.     {
  920.         // extract substring which is command number in string form
  921.         
  922.         BlockMoveData( &iText[i + 1], &subStr[1], iText[0] - i );
  923.         subStr[0] = iText[0] - i;
  924.         
  925.         StringToNum( subStr, aCmd );
  926.         
  927.         // truncate string
  928.         
  929.         iText[0] = i - 1;
  930.     }
  931. }    
  932.  
  933. /*------------------------------***  APPENDSTDITEMS  ***-------------------------------*/
  934. /*
  935. appends DA names or FONT names to the menu
  936. ---------------------------------------------------------------------------------------*/
  937.  
  938. void        ZMenuBar::AppendStdItems( const short menuID, const short iType )
  939. {
  940.     MenuHandle    mH;
  941.     
  942.     mH = FindMenuID( menuID );
  943.     
  944.     if ( mH )
  945.         AppendResMenu( mH, ( iType == appendDANames )? 'DRVR' : 'FONT' );
  946. }    
  947.  
  948.  
  949. /*----------------------------------***  FINDMCMD  ***---------------------------------*/
  950. /*
  951. find the command entry associated with the mSelection value.
  952. ---------------------------------------------------------------------------------------*/
  953.  
  954. void        ZMenuBar::FindMCmd( const long mSelect, MenuCmd* aCmd )
  955. {
  956.     long        m;
  957.     MenuCmd        mCmd;
  958.     
  959.     for( m = 1; m <= theMenuCmds->CountItems(); m++ )
  960.     {
  961.         theMenuCmds->GetArrayItem( &mCmd, m );
  962.     
  963.         // is this the one we want?
  964.         
  965.         if ( HiWord( mSelect ) == mCmd.menuID &&
  966.              LoWord( mSelect ) == mCmd.itemID )
  967.         {
  968.             *aCmd = mCmd;
  969.             break;
  970.         }
  971.     }    
  972. }    
  973.  
  974.  
  975. /*--------------------------------***  FINDCOMMAND  ***--------------------------------*/
  976. /*
  977. inverse operation- given a command, return the original menu item and ID.
  978. ---------------------------------------------------------------------------------------*/
  979.  
  980. void        ZMenuBar::FindCommand( const long cmd, short* menuID, short* itemID )    
  981. {
  982.     long        m;
  983.     MenuCmd        mCmd;
  984.     
  985.     for( m = 1; m <= theMenuCmds->CountItems(); m++ )
  986.     {
  987.         theMenuCmds->GetArrayItem( &mCmd, m );
  988.     
  989.         // is this the one we want?
  990.         
  991.         if ( cmd == mCmd.theCmd )
  992.         {
  993.             *menuID = mCmd.menuID;
  994.             *itemID = mCmd.itemID;
  995.             break;
  996.         }
  997.     }    
  998. }
  999.  
  1000.  
  1001. /*------------------------------***  SETTITLEHILITE  ***-------------------------------*/
  1002. /*
  1003. set the menu title hilite on or off
  1004. ---------------------------------------------------------------------------------------*/
  1005.  
  1006. void        ZMenuBar::SetTitleHilite( const short menuID, const Boolean state )
  1007. {
  1008.     if ( state )
  1009.         HiliteMenu( menuID );
  1010.     else
  1011.         HiliteMenu( 0 );
  1012. }
  1013.  
  1014.  
  1015. /*--------------------------------***  FINDMENUID  ***---------------------------------*/
  1016. /*
  1017. returns the handle of the menu with the given ID, even if it has not been inserted into
  1018. the system list. This can only find menus that belong to this bar object however.
  1019. ---------------------------------------------------------------------------------------*/
  1020.  
  1021. MenuHandle    ZMenuBar::FindMenuID( const short menuID )
  1022. {
  1023.     // returns the handle of the menu with the given ID. This works whether or not the menu
  1024.     // was actually installed in the system menu list. If so, we use the toolbox call. If not
  1025.     // we use the slightly slower method of looking through our private array of commands to
  1026.     // locate the menu.
  1027.     
  1028.     MenuInfRec    mRec;
  1029.     
  1030.     mRec.macMenu = GetMenuHandle( menuID );
  1031.     
  1032.     if ( mRec.macMenu == NULL )
  1033.         FindMenuInfo( menuID, &mRec );
  1034.     
  1035.     return mRec.macMenu;
  1036. }
  1037.  
  1038.  
  1039. /*-------------------------------***  FINDMENUINFO  ***--------------------------------*/
  1040. /*
  1041. returns the info record forthe given menu ID. This info is used to determine the right
  1042. dimming and disposal behaviour for the menu.
  1043. ---------------------------------------------------------------------------------------*/
  1044.  
  1045. void        ZMenuBar::FindMenuInfo( const short menuID, MenuInfRec* mRec )    
  1046. {
  1047.     short        n, i;
  1048.     MenuInfRec    mr;
  1049.     
  1050.     n = theMenus->CountItems();
  1051.     
  1052.     for( i = 1; i<= n; i++ )
  1053.     {
  1054.         theMenus->GetArrayItem( &mr, i );    
  1055.     
  1056.         if ( mr.menuID == menuID )
  1057.         {
  1058.             *mRec = mr;
  1059.             break;
  1060.         }
  1061.     }
  1062. }
  1063.  
  1064.